﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using BackTask.Core.Util;
using BackTask.Model;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace BackTask.Json
{
    public class JsonConfiguration : BackTaskConfiguration
    {
        internal string jsonFilePath;

        private readonly ConfigurationBuilder Builder;
        public override StorageTypes StorageType { get => StorageTypes.Json; }
        protected override List<TaskJob> Jobs { get; set; } = new List<TaskJob>();

        internal IConfigurationRoot Root;

        public JsonConfiguration()
        {
            this.Builder = new ConfigurationBuilder();
        }

        public override IEnumerable<TaskJob> BindJobs()
        {
            IEnumerable<IConfigurationSection> list = this.Root.GetSection("Task").GetChildren();

            foreach (var item in list)
            {
                IConfigurationSection type = item.GetSection("type");
                TaskJob job;
                if (type?.Value?.ToLower() == TaskBuilderTypes.Cron.ToString().ToLower())
                {
                    job = BindCronTaskJob(item);
                }
                else if (type?.Value?.ToLower() == TaskBuilderTypes.Simple.ToString().ToLower())
                {
                    job = BindSimpleTaskJob(item);
                }
                else
                {
                    throw new Exception("unsupport task type");
                }
                Jobs.Add(job);
            }

            return Jobs;
        }

        public override async void DeleteConfiguration(string name, string groupName)
        {
            var willDeleteJob = Jobs.FirstOrDefault(r => r.Name == name && r.GroupName == groupName);
            Jobs.Remove(willDeleteJob);
            var json = JsonConvert.SerializeObject(Jobs, Formatting.Indented);
            string text = "{\n  \"Task\":" + json + "\n}";
            await File.WriteAllTextAsync(this.jsonFilePath, text, Encoding.UTF8);
        }

        private TaskJob BindCronTaskJob(IConfigurationSection currentSection)
        {
            string url = currentSection.GetSection("url")?.Value;
            if (url.IsNullOrEmptyOrWithSpace()) throw new Exception("request url is null or empty");
            string cron = currentSection.GetSection("cron")?.Value;
            if (url.IsNullOrEmptyOrWithSpace()) throw new Exception("cron expression is empty");
            string name = currentSection.GetSection("name")?.Value;
            string priorityValue = currentSection.GetSection("priority")?.Value;
            string groupNameValue = currentSection.GetSection("groupName")?.Value;
            string groupName = string.IsNullOrEmpty(groupNameValue) ? Constants.DEFAULT_GROUP_NAME : groupNameValue;
            int priority = priorityValue.IsNullOrEmptyOrWithSpace() ? 0 : Convert.ToInt32(priorityValue);

            if (HasSameJob(name, groupName))
            {
                throw new Exception($"name: {name},groupName: {groupName}, job has been added");
            }

            string description = currentSection.GetSection("description")?.Value;

            return new CronTaskJob(cron, url, name, priority, description, groupName);
        }

        private TaskJob BindSimpleTaskJob(IConfigurationSection currentSection)
        {
            string name = currentSection.GetSection("name")?.Value ?? DateTime.Now.ToString();
            string url = currentSection.GetSection("url")?.Value;
            if (url.IsNullOrEmptyOrWithSpace()) throw new Exception("request url is null or empty");

            string groupNameValue = currentSection.GetSection("groupName")?.Value;
            string groupName = string.IsNullOrEmpty(groupNameValue) ? Constants.DEFAULT_GROUP_NAME : groupNameValue;

            if (HasSameJob(name, groupName))
            {
                throw new Exception($"name: {name},groupName: {groupName}, job has been added");
            }

            string executeCountValue = currentSection.GetSection("config:executeCount")?.Value;
            int executeCount = executeCountValue.IsNullOrEmptyOrWithSpace() ? 1 : Convert.ToInt32(executeCountValue);

            string repeatForeverValue = currentSection.GetSection("config:repeatForever")?.Value;
            bool repeatForever = repeatForeverValue.IsNullOrEmptyOrWithSpace() ? false : repeatForeverValue.ToLower() == "true";

            string intervalValue = currentSection.GetSection("config:interval")?.Value;
            int interval = intervalValue.IsNullOrEmptyOrWithSpace() ? 1 : Convert.ToInt32(intervalValue);
            string intervalTypeValue = currentSection.GetSection("config:intervalType")?.Value;
            SimpleTaskJobIntervalTypes intervalType = intervalTypeValue.IsNullOrEmptyOrWithSpace() ? SimpleTaskJobIntervalTypes.Second : intervalTypeValue.GetEnumByDescription<SimpleTaskJobIntervalTypes>();

            string priorityValue = currentSection.GetSection("priority")?.Value;
            int priority = priorityValue.IsNullOrEmptyOrWithSpace() ? 0 : Convert.ToInt32(priorityValue);

            string description = currentSection.GetSection("description")?.Value;

            SimpleTaskExecuteConfig executeConfig = new SimpleTaskExecuteConfig()
            {
                Interval = interval,
                RepeatForever = repeatForever,
                IntervalType = intervalType,
                ExecuteCount = executeCount,
            };

            return new SimpleTaskJob(url, name, executeConfig, priority, description, groupName);
        }

        public override void BuildConfiguration()
        {
            this.Builder.AddJsonFile(jsonFilePath, optional: true, reloadOnChange: true);
            this.Root = this.Builder.Build();
        }

        public override void UseConfiguration(object configuration)
        {
            this.jsonFilePath = Path.Combine(Directory.GetCurrentDirectory(), Convert.ToString(configuration));
        }

        public override async void AddConfiguration(TaskJob job)
        {
            Jobs.Add(job);
            var json = JsonConvert.SerializeObject(Jobs, Formatting.Indented);
            string resultTxt = "{\n  \"Task\":" + json + "\n}";
            await File.WriteAllTextAsync(this.jsonFilePath, resultTxt, Encoding.UTF8);
        }

        public override async void UpdateConfiguration(TaskJob job)
        {
            var willUpdateJob = Jobs.FirstOrDefault(r => r.GroupName == job.GroupName && r.Name == job.Name);
            Jobs.Remove(willUpdateJob);
            Jobs.Add(job);
            var json = JsonConvert.SerializeObject(Jobs, Formatting.Indented);
            string resultTxt = "{\n  \"Task\":" + json + "\n}";
            await File.WriteAllTextAsync(this.jsonFilePath, resultTxt, Encoding.UTF8);
        }
    }
}
